/** @file
Phytium platform smbios Libary.

Copyright (C) 2023, Phytium Technology Co Ltd. All rights reserved.<BR>

SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <PiDxe.h>
#include <Library/ArmLib.h>
#include <Library/ArmSmcLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/PssiLib.h>
#include <Library/PrintLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/ScmiLib.h>
#include <Uefi/UefiBaseType.h>
#include <IndustryStandard/SmBios.h>
#include <Protocol/Smbios.h>
#include <PhytiumSmbiosHelper.h>
#include "PlatformSmbiosLib.h"

#define CPU_COUNT 16
#define DIE_COUTN_PER_SOCKET 4
#define L1_SIZE_PER_CORE  (64 * 1024)
#define L2_SIZE_PER_CORE  (512 * 1024)
#define L3_SIZE_PER_CORE  (512 * 1024)

extern
EFI_STATUS
EFIAPI
LogSmbiosData (
  IN  EFI_SMBIOS_TABLE_HEADER *Template,
  IN  CHAR8                   **StringPack,
  OUT EFI_SMBIOS_HANDLE       *DataSmbiosHandle
  );

SmbiosCpuInfo mSmbiosCpuInfo[] = {
 {0x800, "Phytium Ps2364", 9, 2300},
};
/***********************************************************************
        SMBIOS data definition  TYPE4  Processor Information
************************************************************************/
SMBIOS_TABLE_TYPE4 mProcessorInfoType4 = {
  { EFI_SMBIOS_TYPE_PROCESSOR_INFORMATION, sizeof (SMBIOS_TABLE_TYPE4), 0},
  1,                               // Socket String
  CentralProcessor,                // ProcessorType;          ///< The enumeration value from PROCESSOR_TYPE_DATA.
  ProcessorFamilyIndicatorFamily2, // ProcessorFamily;        ///< The enumeration value from PROCESSOR_FAMILY2_DATA.
  2,                               // ProcessorManufacture String;
  {                                // ProcessorId;
    { 0x00, 0x00, 0x00, 0x00 },
    { 0x00, 0x00, 0x00, 0x00 }
  },
  3,                    // ProcessorVersion String;
  {                     // Voltage;
    0,  // ProcessorVoltageCapability5V        :1;
    0,  // ProcessorVoltageCapability3_3V      :1;
    0,  // ProcessorVoltageCapability2_9V      :1;
    0,  // ProcessorVoltageCapabilityReserved  :1; ///< Bit 3, must be zero.
    0,  // ProcessorVoltageReserved            :3; ///< Bits 4-6, must be zero.
    0   // ProcessorVoltageIndicateLegacy      :1;
  },
  48,                     // ExternalClock;
  0,                      // MaxSpeed;
  0,                      // CurrentSpeed;
  0x41,                   // Status;
  ProcessorUpgradeNone,   // ProcessorUpgrade;         ///< The enumeration value from PROCESSOR_UPGRADE.
  0xFFFF,                 // L1CacheHandle;
  0xFFFF,                 // L2CacheHandle;
  0xFFFF,                 // L3CacheHandle;
  4,                      // SerialNumber;
  5,                      // AssetTag;
  6,                      // PartNumber;
  0,                      // CoreCount;
  0,                      // EnabledCoreCount;
  0,                      // ThreadCount;
  0x6C,                   // ProcessorCharacteristics; ///< The enumeration value from PROCESSOR_CHARACTERISTIC_FLAGS
      // ProcessorReserved1              :1;
      // ProcessorUnknown                :1;
      // Processor64BitCapble            :1;
      // ProcessorMultiCore              :1;
      // ProcessorHardwareThread         :1;
      // ProcessorExecuteProtection      :1;
      // ProcessorEnhancedVirtualization :1;
      // ProcessorPowerPerformanceCtrl    :1;
      // Processor128bitCapble            :1;
      // ProcessorReserved2               :7;
  ProcessorFamilyARM,     // ARM Processor Family;
  0,                      // CoreCount2;
  0,                      // EnabledCoreCount2;
  0,                      // ThreadCount2;
};

CHAR8 mSocketDesignation[128];
CHAR8 mProcessorVersion[128];
CHAR8 mProcessorSerialNumber[128];

CHAR8 *mProcessorInfoType4Strings[] = {
  mSocketDesignation,
  "PHYTIUM LTD",
  mProcessorVersion,
  mProcessorSerialNumber,
  "Not Set",
  "Not Set",
  NULL
};

/***********************************************************************
        SMBIOS data definition  TYPE7  Cache Information
************************************************************************/
SMBIOS_TABLE_TYPE7 mCacheInfoType7L1I = {
  { EFI_SMBIOS_TYPE_CACHE_INFORMATION, sizeof (SMBIOS_TABLE_TYPE7), 0 },
  1,                        // SocketDesignation String
  0x380,                    // Cache Configuration
       //Cache Level        :3  (L1)
       //Cache Socketed     :1  (Not Socketed)
       //Reserved           :1
       //Location           :2  (Internal)
       //Enabled/Disabled   :1  (Enabled)
       //Operational Mode   :2  (Unkonwn)
       //Reserved           :6
  0x0000,                   // Maximum Size
  0x0000,                   // Install Size
  {                         // Supported SRAM Type
    0,  //Other             :1
    0,  //Unknown           :1
    0,  //NonBurst          :1
    0,  //Burst             :1
    0,  //PiplelineBurst    :1
    1,  //Synchronous       :1
    0,  //Asynchronous      :1
    0   //Reserved          :9
  },
  {                         // Current SRAM Type
    0,  //Other             :1
    0,  //Unknown           :1
    0,  //NonBurst          :1
    0,  //Burst             :1
    0,  //PiplelineBurst    :1
    1,  //Synchronous       :1
    0,  //Asynchronous      :1
    0   //Reserved          :9
  },
  0,                        // Cache Speed unknown
  CacheErrorSingleBit,      // Error Correction
  CacheTypeInstruction,     // System Cache Type
  CacheAssociativityOther   // Associativity
};

CHAR8 mL1IDesignation[128];

CHAR8  *mCacheInfoType7StringsL1I[] = {
  mL1IDesignation,
  NULL
};

SMBIOS_TABLE_TYPE7 mCacheInfoType7L1D = {
  { EFI_SMBIOS_TYPE_CACHE_INFORMATION, sizeof (SMBIOS_TABLE_TYPE7), 0 },
  1,                        // SocketDesignation String
  0x180,                    // Cache Configuration
       //Cache Level        :3  (L1)
       //Cache Socketed     :1  (Not Socketed)
       //Reserved           :1
       //Location           :2  (Internal)
       //Enabled/Disabled   :1  (Enabled)
       //Operational Mode   :2  (WB)
       //Reserved           :6
  0x0000,                   // Maximum Size
  0x0000,                   // Install Size
  {                         // Supported SRAM Type
    0,  //Other             :1
    0,  //Unknown           :1
    0,  //NonBurst          :1
    0,  //Burst             :1
    0,  //PiplelineBurst    :1
    1,  //Synchronous       :1
    0,  //Asynchronous      :1
    0   //Reserved          :9
  },
  {                         // Current SRAM Type
    0,  //Other             :1
    0,  //Unknown           :1
    0,  //NonBurst          :1
    0,  //Burst             :1
    0,  //PiplelineBurst    :1
    1,  //Synchronous       :1
    0,  //Asynchronous      :1
    0   //Reserved          :9
  },
  0,                        // Cache Speed unknown
  CacheErrorSingleBit,      // Error Correction
  CacheTypeData,            // System Cache Type
  CacheAssociativityOther   // Associativity
};

CHAR8 mL1DDesignation[128];
CHAR8 *mCacheInfoType7StringsL1D[] = {
  mL1DDesignation,
  NULL
};

SMBIOS_TABLE_TYPE7 mCacheInfoType7L2 = {
  { EFI_SMBIOS_TYPE_CACHE_INFORMATION, sizeof (SMBIOS_TABLE_TYPE7), 0 },
  1,                        // SocketDesignation String
  0x0181,                   // Cache Configuration
       //Cache Level        :3  (L2)
       //Cache Socketed     :1  (Not Socketed)
       //Reserved           :1
       //Location           :2  (Internal)
       //Enabled/Disabled   :1  (Enabled)
       //Operational Mode   :2  (WB)
       //Reserved           :6
  0x0000,                   // Maximum Size
  0x0000,                   // Install Size
  {                         // Supported SRAM Type
    0,  //Other             :1
    0,  //Unknown           :1
    0,  //NonBurst          :1
    0,  //Burst             :1
    0,  //PiplelineBurst    :1
    1,  //Synchronous       :1
    0,  //Asynchronous      :1
    0   //Reserved          :9
  },
  {                         // Current SRAM Type
    0,  //Other             :1
    0,  //Unknown           :1
    0,  //NonBurst          :1
    0,  //Burst             :1
    0,  //PiplelineBurst    :1
    1,  //Synchronous       :1
    0,  //Asynchronous      :1
    0   //Reserved          :9
  },
  0,                        // Cache Speed unknown
  CacheErrorSingleBit,      // Error Correction Multi
  CacheTypeUnified,         // System Cache Type
  CacheAssociativityOther   // Associativity
};

CHAR8 mL2Designation[128];
CHAR8  *mCacheInfoType7StringsL2[] = {
  mL2Designation,
  NULL
};

SMBIOS_TABLE_TYPE7 mCacheInfoType7L3 = {
  { EFI_SMBIOS_TYPE_CACHE_INFORMATION, sizeof (SMBIOS_TABLE_TYPE7), 0 },
  1,                        // SocketDesignation String
  0x0182,                   // Cache Configuration
       //Cache Level        :3  (L2)
       //Cache Socketed     :1  (Not Socketed)
       //Reserved           :1
       //Location           :2  (Internal)
       //Enabled/Disabled   :1  (Enabled)
       //Operational Mode   :2  (WB)
       //Reserved           :6
  0x0000,                   // Maximum Size
  0x0000,                   // Install Size
  {                         // Supported SRAM Type
    0,  //Other             :1
    0,  //Unknown           :1
    0,  //NonBurst          :1
    0,  //Burst             :1
    0,  //PiplelineBurst    :1
    1,  //Synchronous       :1
    0,  //Asynchronous      :1
    0   //Reserved          :9
  },
  {                         // Current SRAM Type
    0,  //Other             :1
    0,  //Unknown           :1
    0,  //NonBurst          :1
    0,  //Burst             :1
    0,  //PiplelineBurst    :1
    1,  //Synchronous       :1
    0,  //Asynchronous      :1
    0   //Reserved          :9
  },
  0,                        // Cache Speed unknown
  CacheErrorSingleBit,      // Error Correction Multi
  CacheTypeUnified,         // System Cache Type
  CacheAssociativityOther   // Associativity
};

CHAR8 mL3Designation[128];
CHAR8  *mCacheInfoType7StringsL3[] = {
  mL3Designation,
  NULL
};

/**
 Get max cpu speed.

 @param [in]  CpuTypeId    Cpu Type Id

 @return Max speed.
**/
UINT16
GetCpuMaxSpeed (
  IN UINTN CpuTypeId
  )
{
  UINT8 Index;
  UINT16 CpuMaxSpeed;

  for(Index = 0; Index < sizeof(mSmbiosCpuInfo)/sizeof(mSmbiosCpuInfo); Index++) {
    if(mSmbiosCpuInfo[Index].CpuTypeId == CpuTypeId) {
      CpuMaxSpeed = mSmbiosCpuInfo[Index].MaxSpeed;
      break;
    }
  }

  return CpuMaxSpeed;
}

#define SCMI_PROTOCOL_ID_CPU 0x13
#define SCMI_MESSAGE_ID_CPU  0x8
/**
 Get cpu current speed through scmi prototol.

 @return Current speed,uint is MHz.
**/
UINT32
GetCpuCurrentSpeed (
  VOID
  )
{
  UINT32             PayLoad[3];
  UINT32             Len;
  UINT32             DomainId;
  UINT32             Freq;
  EFI_STATUS         Status;
  SCMI_ADDRESS_BASE  Base;

  Base.MhuConfigBase = PcdGet64 (PcdMhuConfigBaseAddress);
  Base.MhuBase = PcdGet64 (PcdMhuBaseAddress);
  Base.ShareMemoryBase = PcdGet64 (PcdMhuShareMemoryBase);

  DomainId = 0x0;
  Status = ScmiCommandExecute (
             &Base,
             SCMI_PROTOCOL_ID_CPU,
             SCMI_MESSAGE_ID_CPU,
             1,
             &DomainId,
             &Len,
             PayLoad
             );
  if (EFI_ERROR (Status)) {
    return 0;
  }

  Freq = PayLoad[1] / 1000;

  return Freq;
}

/**
 update processor version.

 @param [in]  CpuTypeId    CpuType Id

**/
VOID
UpdateProcessorVersion (
  IN UINTN CpuTypeId
  )
{
  UINT8 Index;

  for(Index = 0; Index < sizeof(mSmbiosCpuInfo)/sizeof(SmbiosCpuInfo); Index++) {
    if(mSmbiosCpuInfo[Index].CpuTypeId == CpuTypeId) {
      CopyMem(mProcessorVersion, mSmbiosCpuInfo[Index].CpuTypeName, AsciiStrLen(mSmbiosCpuInfo[Index].CpuTypeName));
      break;
    }
  }
}

/**
 update processor version.

 @param [in]  CpuTypeId    CpuType Id

**/
VOID
UpdateProcessorVoltage (
  IN UINTN CpuTypeId
  )
{
  UINT8  Index;
  UINT16 *Voltage;

  for(Index = 0; Index < sizeof(mSmbiosCpuInfo)/sizeof(SmbiosCpuInfo); Index++) {
    if(mSmbiosCpuInfo[Index].CpuTypeId == CpuTypeId) {
      Voltage = (UINT16 *)&(mProcessorInfoType4.Voltage);
      *Voltage = 0x80 + mSmbiosCpuInfo[Index].CpuVoltage;
      break;
    }
  }
}

/**
 Get Cpu count Info.

 @param [out]  CountInfo  The pointer to CpuCountInfo struct

**/
VOID
GetCoreCount (
  OUT CPU_COUNT_INFO  *CountInfo
  )
{
  PHYTIUM_CPU_MAP_INFO  *CpuMapInfo;
  UINT64                *CpuMap;
  UINT32                CoreCount;
  UINT16                CoreIndex;
  UINT8                 DieIndex;
  UINT8                 DieCount;
  UINT8                 SocketIndex;
  UINTN                 Size;

  CoreCount = 0;
  SocketIndex = 0;
  Size = EFI_PAGE_SIZE;

  CpuMapInfo = (PHYTIUM_CPU_MAP_INFO *)AllocatePages(1);
  if (NULL == CpuMapInfo) {
    DEBUG ((DEBUG_ERROR,"Allocate CpuMapInfo Fail\n"));
    while(1);
  }
  ZeroMem(CpuMapInfo, EFI_PAGE_SIZE);
  PssiGetCpuMapsInfo(CpuMapInfo, &Size);
  CpuMap = CpuMapInfo->CpuMap;
  DieCount = CpuMapInfo->CpuMapCount;

  CountInfo->SocketCount = 0x1;
  for (DieIndex = 0; DieIndex < DieCount; DieIndex++) {
    if (((DieIndex % DIE_COUTN_PER_SOCKET) == 0) && (DieIndex != 0)) {
      CountInfo->CoreCount[SocketIndex] = CoreCount;
      CountInfo->SocketCount++;
      SocketIndex++;
      CoreCount = 0;
    }
    for (CoreIndex = 0; CoreIndex < CPU_COUNT; CoreIndex++) {
      if ((CpuMap[DieIndex] >> CoreIndex) & 0x1) {
        CoreCount++;
      }
    }
    if (DieIndex == (DieCount -1)) {
      CountInfo->CoreCount[SocketIndex] = CoreCount;
    }
  }

  FreePages(CpuMapInfo, 1);
}

/**
  SMBIOS data update  TYPE7  Cache Information.

  @param [in]  SocketIndex  Socket Index.
  @param [in]  CoreCount    Core Count of socket.
**/
VOID
CacheInfoUpdateSmbiosType7 (
  UINT8 SocketIndex,
  UINT16 CoreCount
  )
{
  EFI_SMBIOS_HANDLE    SmbiosHandle;
  UINT16               L1Size;
  UINT16               L2Size;
  UINT16               L3Size;

  L1Size = 0x8000 | (CoreCount * L1_SIZE_PER_CORE / 64 / 1024);
  L2Size = 0x8000 | (CoreCount * L2_SIZE_PER_CORE / 64 / 1024);
  L3Size = 0x8000 | (CoreCount * L3_SIZE_PER_CORE / 64 / 1024);

  mCacheInfoType7L1I.MaximumCacheSize = L1Size;
  mCacheInfoType7L1I.InstalledSize = L1Size;
  mCacheInfoType7L1I.MaximumCacheSize2 = L1Size;
  mCacheInfoType7L1I.InstalledSize2 = L1Size;

  AsciiSPrint(mL1IDesignation, SMBIOS_STRING_MAX_LENGTH, "SOCKET %d L1 Instruction", SocketIndex);
  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER*)&mCacheInfoType7L1I, mCacheInfoType7StringsL1I, NULL);

  mCacheInfoType7L1D.MaximumCacheSize = L1Size;
  mCacheInfoType7L1D.InstalledSize = L1Size;
  mCacheInfoType7L1D.MaximumCacheSize2 = L1Size;
  mCacheInfoType7L1D.InstalledSize2 = L1Size;
  AsciiSPrint(mL1DDesignation, SMBIOS_STRING_MAX_LENGTH, "SOCKET %d L1 Data", SocketIndex);
  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER*)&mCacheInfoType7L1D, mCacheInfoType7StringsL1D, &SmbiosHandle);
  // Set Type4 L1CacheHandle to point to the newly added L1 Data Cache
  mProcessorInfoType4.L1CacheHandle = (UINT16) SmbiosHandle;
  mCacheInfoType7L2.MaximumCacheSize = L2Size;
  mCacheInfoType7L2.InstalledSize = L2Size;
  mCacheInfoType7L2.MaximumCacheSize2 = L2Size;
  mCacheInfoType7L2.InstalledSize2 = L2Size;
  AsciiSPrint(mL2Designation, SMBIOS_STRING_MAX_LENGTH, "SOCKET %d L2 Unified", SocketIndex);
  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER*)&mCacheInfoType7L2, mCacheInfoType7StringsL2, &SmbiosHandle);
  // Set Type4 L2CacheHandle to point to the newly added L2 Cache
  mProcessorInfoType4.L2CacheHandle = (UINT16) SmbiosHandle;

  mCacheInfoType7L3.MaximumCacheSize = L3Size;
  mCacheInfoType7L3.InstalledSize = L3Size;
  mCacheInfoType7L3.MaximumCacheSize2 = L3Size;
  mCacheInfoType7L3.InstalledSize2 = L3Size;
  AsciiSPrint(mL3Designation, SMBIOS_STRING_MAX_LENGTH, "SOCKET %d L3 Unified", SocketIndex);
  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER*)&mCacheInfoType7L3, mCacheInfoType7StringsL3, &SmbiosHandle);
  // Set Type4 L3CacheHandle to point to the newly added L3 Cache
  mProcessorInfoType4.L3CacheHandle = (UINT16) SmbiosHandle;
}



/***********************************************************************
        SMBIOS data update  TYPE4  Processor Information.
************************************************************************/
VOID
ProcessorInfoUpdateSmbiosType4 (
  VOID
  )
{
  UINT8                    SocketIndex;
  UINT8                    SocketCount;
  UINT16                   CoreCount;
  UINT64                   *ProcessorId;
  CPU_COUNT_INFO           CountInfo;
  PHYTIUM_CPU_VERSION      CpuVersion;
  ARM_SMC_ARGS             ArmSmcArgs;
  BOOLEAN                  SupportSocId;
  INT64                    SocVersion;
  INT64                    SocRevision;
  INT64                    SocId;

  SupportSocId = FALSE;

  ZeroMem (&CountInfo, sizeof (CPU_COUNT_INFO));
  GetCoreCount (&CountInfo);
  SocketCount = CountInfo.SocketCount;

  for (SocketIndex = 0; SocketIndex < SocketCount; SocketIndex++) {
    CoreCount = CountInfo.CoreCount[SocketIndex];
    DEBUG ((DEBUG_INFO, "SocketIndex[%d] Core Count:%d\n", SocketIndex, CoreCount));
    //First,install the cache table
    CacheInfoUpdateSmbiosType7 (SocketIndex, CoreCount);
    if (CoreCount < 256) {
      mProcessorInfoType4.CoreCount = (UINT8)CoreCount;
      mProcessorInfoType4.CoreCount2 = CoreCount;
      mProcessorInfoType4.EnabledCoreCount = (UINT8)CoreCount;
      mProcessorInfoType4.EnabledCoreCount2 = CoreCount;
      mProcessorInfoType4.ThreadCount = (UINT8)CoreCount;
      mProcessorInfoType4.ThreadCount2 = CoreCount;
    } else {
      mProcessorInfoType4.CoreCount = 0xFF;
      mProcessorInfoType4.CoreCount2 = CoreCount;
      mProcessorInfoType4.EnabledCoreCount = 0xFF;
      mProcessorInfoType4.EnabledCoreCount2 = CoreCount;
      mProcessorInfoType4.ThreadCount = 0xFF;
      mProcessorInfoType4.ThreadCount2 = CoreCount;
    }

    AsciiSPrint(mSocketDesignation, SMBIOS_STRING_MAX_LENGTH, "SOCKET %d", SocketIndex);
    PssiGetCpuVersion (SocketIndex, &CpuVersion);
    UpdateProcessorVersion (CpuVersion.CpuType);
    UpdateProcessorVoltage (CpuVersion.CpuType);
    if ((CpuVersion.CpuId[0] != 0) && (CpuVersion.CpuId[1] != 0)) {
      ZeroMem (mProcessorSerialNumber, sizeof (mProcessorSerialNumber));
      CopyMem (mProcessorSerialNumber, &CpuVersion.CpuId[0], 8);
      CopyMem (&mProcessorSerialNumber[8], &CpuVersion.CpuId[1], 8);
    } else {
      AsciiStrCpyS (mProcessorSerialNumber, sizeof (mProcessorSerialNumber), "Unknown");
    }

    //Get SocID
    ZeroMem (&ArmSmcArgs, sizeof (ARM_SMC_ARGS));
    ArmSmcArgs.Arg0 = 0x80000002;
    ArmSmcArgs.Arg1 = 0x0;
    ArmCallSmc (&ArmSmcArgs);

    if (ArmSmcArgs.Arg0 > 0) {
      SocVersion = ArmSmcArgs.Arg0;
      SupportSocId = TRUE;
    }

    if (SupportSocId == TRUE) {
      ZeroMem (&ArmSmcArgs, sizeof (ARM_SMC_ARGS));
      ArmSmcArgs.Arg0 = 0x80000002;
      ArmSmcArgs.Arg1 = 0x1;
      ArmCallSmc (&ArmSmcArgs);
      SocRevision = ArmSmcArgs.Arg0;
      SocId = (SocRevision << 32) | SocVersion;
      ProcessorId = (UINT64 *)&(mProcessorInfoType4.ProcessorId);
      *ProcessorId = SocId;
      mProcessorInfoType4.ProcessorCharacteristics = 0x26c;
    }

    if (SupportSocId == FALSE) {
      ProcessorId = (UINT64 *)&(mProcessorInfoType4.ProcessorId);
      *ProcessorId = ArmReadMidr();
    }
    mProcessorInfoType4.MaxSpeed = GetCpuMaxSpeed (CpuVersion.CpuType);
    mProcessorInfoType4.CurrentSpeed = GetCpuCurrentSpeed ();

    LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER*)&mProcessorInfoType4, mProcessorInfoType4Strings, NULL);
  }
}

/**
  Platform Entry.
**/
VOID PlatSmbiosEntry(
  VOID
  )
{
  ProcessorInfoUpdateSmbiosType4 ();
}
